package work.soho.proxy.socket5;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Arrays;

/**
 * socket5 连接处理线程
 *
 * TODO 里面只只进行了简单的socket5协议， 支持并不完善； 待完善
 */
public class Socket5Thread extends Thread {
    private Socket socketIn;
    private InputStream isIn;
    private OutputStream osIn;
    private String sshHost;
    private int sshPort;
    private String sshUser;
    private String sshPrivateKeyPath;
    private String sshPrivateKeyPassword;

    public Socket5Thread(Socket socket, String sshHost, int sshPort, String sshUser
            , String sshPublicKeyPath, String sshPrivateKeyPassword) {
        this.socketIn = socket;
        this.sshHost = sshHost;
        this.sshPort = sshPort;
        this.sshUser = sshUser;
        this.sshPrivateKeyPath = sshPublicKeyPath;
        this.sshPrivateKeyPassword = sshPrivateKeyPassword;
    }

    private byte[] buffer = new byte[4096];
    private static final byte[] VER = { 0x5, 0x0 };
    private static final byte[] CONNECT_OK = { 0x5, 0x0, 0x0, 0x1, 0, 0, 0, 0, 0, 0 };

    public void run() {
        try {
            System.out.println("\n\nClient connect at: " + socketIn.getInetAddress() + ":" + socketIn.getPort());
            isIn = socketIn.getInputStream();
            osIn = socketIn.getOutputStream();
            int len = isIn.read(buffer, 0, 3);
            System.out.println("< " + bytesToHexString(buffer, 0, len));
            osIn.write(VER);
            osIn.flush();
            System.out.println("> " + bytesToHexString(VER, 0, VER.length));
            len = isIn.read(buffer, 0, 4);
            System.out.println("< " + bytesToHexString(buffer, 0, len));
            //TODO 判断是IP还是域名
            String host = "";
            if(buffer[3] == 3) {
                //读取域名信息
                isIn.read(buffer, 0, 1);
                int domainLength = buffer[0];
                isIn.read(buffer, 0, domainLength);
                host = new String(Arrays.copyOfRange(buffer, 0, domainLength));
            } else if(buffer[3] == 1) {
                //ipv4
                isIn.read(buffer, 0, 4);
                host = ipAddressBytesToString(buffer);
            } else {
                System.out.println("不支持的连接方式");
            }

            isIn.read(buffer, 0, 2);
            int port = findPort(buffer, 0, 1);
            System.out.println("Client connect to " + host + ":" + port);
            //同客户端确认连接成功， 可以进修业务通信了
            osIn.write(CONNECT_OK);

            StreamForwarding.forwarding(sshHost, sshPort, sshUser, sshPrivateKeyPath,
                    sshPrivateKeyPassword,
                    isIn, osIn, host, port);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("a client leave");
            if(socketIn!=null) {
                //尝试关闭客户端连接
                try {
                    socketIn.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }

    public static String ipAddressBytesToString(byte[] ipAddressBytes) {
        return (ipAddressBytes[0] & 0XFF) + "." + (ipAddressBytes[1] & 0XFF) + "." + (ipAddressBytes[2] & 0XFF) + "." + (ipAddressBytes[3] & 0XFF);
    }

    public static String findHost(byte[] bArray, int begin, int end) {
        StringBuffer sb = new StringBuffer();
        for (int i = begin; i <= end; i++) {
            sb.append(Integer.toString(0xFF & bArray[i]));
            sb.append(".");
        }
        sb.deleteCharAt(sb.length() - 1);
        return sb.toString();
    }

    /**
     * 获取代理端口
     *
     * @param bArray
     * @param begin
     * @param end
     * @return
     */
    public static int findPort(byte[] bArray, int begin, int end) {
        return (bArray[begin] & 0xff) << 8 | (bArray[end] & 0xff);
    }

    // 4A 7D EB 69
    // 74 125 235 105
    public static final String bytesToHexString(byte[] bArray, int begin, int end) {
        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = begin; i < end; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2)
                sb.append(0);
            sb.append(sTemp.toUpperCase());
            sb.append(" ");
        }
        return sb.toString();
    }
}